package org.shreker.plugins.test.bean;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * JDBC操作元数据示例-- DatabaseMetaData接口
 */
public class JDBCConnector {
    
    private DatabaseMetaData metaData;
    
    private Connection connection = null;
    
    public JDBCConnector(String driverClassName, String url, String username, String password) {
        try {
            //连接数据库
            Class.forName(driverClassName);
            connection = DriverManager.getConnection(url, username, password);
            connection.setAutoCommit(true);
            metaData = connection.getMetaData();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    //关闭连接
    public void close(Object o) {
        if (o == null) {
            return;
        }
        if (o instanceof ResultSet) {
            try {
                ((ResultSet) o).close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } else if (o instanceof Statement) {
            try {
                ((Statement) o).close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } else if (o instanceof Connection) {
            Connection c = (Connection) o;
            try {
                if (!c.isClosed()) {
                    c.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    public void close(ResultSet resultSet, Statement stmt, Connection conn) {
        close(resultSet);
        close(stmt);
        close(conn);
    }
    
    public void close(ResultSet resultSet, Connection conn) {
        close(resultSet);
        close(conn);
    }
    
    /**
     * 获取数据库相关信息
     */
    public DataBaseInfo getDataBaseInfo() {
        ResultSet resultSet = null;
        DataBaseInfo dataBaseInfo = new DataBaseInfo();
        try {
            dataBaseInfo.setUserName(metaData.getUserName());
            dataBaseInfo.setSystemFunctions(metaData.getSystemFunctions());
            dataBaseInfo.setTimeDateFunctions(metaData.getTimeDateFunctions());
            dataBaseInfo.setStringFunctions(metaData.getStringFunctions());
            dataBaseInfo.setSchemaTerm(metaData.getSchemaTerm());
            dataBaseInfo.setUrl(metaData.getURL());
            dataBaseInfo.setReadOnly(metaData.isReadOnly());
            dataBaseInfo.setDatabaseProductName(metaData.getDatabaseProductName());
            dataBaseInfo.setDatabaseProductVersion(metaData.getDatabaseProductVersion());
            dataBaseInfo.setDriverName(metaData.getDriverName());
            dataBaseInfo.setDriverVersion(metaData.getDriverVersion());
            resultSet = metaData.getTableTypes();
            List<String> tableTypes = new ArrayList<>();
            while (resultSet.next()) {
                System.out.println(resultSet.getString("TABLE_TYPE"));
                tableTypes.add(resultSet.getString("TABLE_TYPE"));
            }
            dataBaseInfo.setTableTypes(tableTypes);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
        return dataBaseInfo;
    }
    
    /**
     * 获得数据库中所有Schemas(对应于oracle中的Tablespace)
     */
    public void getSchemasInfo() {
        ResultSet resultSet = null;
        try {
            resultSet = metaData.getSchemas();
            while (resultSet.next()) {
                String tableSchem = resultSet.getString("TABLE_SCHEM");
                System.out.println(tableSchem);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
    }
    
    /**
     * 获取数据库中所有的表信息
     */
    public void getTablesList() {
        ResultSet resultSet = null;
        try {
            /*
             * 设置连接属性,使得可获取到表的REMARK(备注)
             */
            String[] types = {"TABLE"};
            resultSet = metaData.getTables(null, null, "%", types);
            while (resultSet.next()) {
                String tableName = resultSet.getString("TABLE_NAME");  //表名
                String tableType = resultSet.getString("TABLE_TYPE");  //表类型
                String remarks = resultSet.getString("REMARKS");       //表备注
                System.out.println(tableName + " - " + tableType + " - " + remarks);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
    }
    
    /**
     * 获取所有表信息
     */
    public void getTableInfo() {
        ResultSet resultSet = null;
        try {
            /*
             * 获取给定类别中使用的表的描述。
             * 方法原型:ResultSet getTables(String catalog,String schemaPattern,String tableNamePattern,String[] types);
             * catalog - 表所在的类别名称;""表示获取没有类别的列,null表示获取所有类别的列。
             * schema - 表所在的模式名称(oracle中对应于Tablespace);""表示获取没有模式的列,null标识获取所有模式的列; 可包含单字符通配符("_"),或多字符通配符("%");
             * tableNamePattern - 表名称;可包含单字符通配符("_"),或多字符通配符("%"); "CUST_INTER_TF_SERVICE_REQ"
             * types - 表类型数组; "TABLE"、"VIEW"、"SYSTEM TABLE"、"GLOBAL TEMPORARY"、"LOCAL TEMPORARY"、"ALIAS" 和 "SYNONYM";null表示包含所有的表类型;可包含单字符通配符("_"),
             * 或多字符通配符("%");
             */
            resultSet = metaData.getTables(null, null, null, new String[]{"TABLE"});
            
            while (resultSet.next()) {
                TableInfo tableInfo = new TableInfo();
                String tableCat = resultSet.getString("TABLE_CAT");  //表类别(可为null)
                tableInfo.setTableCat(tableCat);
                String tableSchema = resultSet.getString("TABLE_SCHEM");//表模式（可能为空）,在oracle中获取的是命名空间,其它数据库未知
                tableInfo.setTableSchem(tableSchema);
                String tableName = resultSet.getString("TABLE_NAME");  //表名
                tableInfo.setTableName(tableName);
                String tableType = resultSet.getString("TABLE_TYPE");  //表类型,典型的类型是 "TABLE"、"VIEW"、"SYSTEM TABLE"、"GLOBAL TEMPORARY"、"LOCAL TEMPORARY"、"ALIAS" 和 "SYNONYM"。
                String remarks = resultSet.getString("REMARKS");       //表备注
                
                System.out.println(tableCat + " - " + tableSchema + " - " + tableName + " - " + tableType + " - " + remarks);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
    }
    
    /**
     * 获取表主键信息
     */
    public void getPrimaryKeysInfo() {
        ResultSet resultSet = null;
        try {
            /*
             * 获取对给定表的主键列的描述
             * 方法原型:ResultSet getPrimaryKeys(String catalog,String schema,String table);
             * catalog - 表所在的类别名称;""表示获取没有类别的列,null表示获取所有类别的列。
             * schema - 表所在的模式名称(oracle中对应于Tablespace);""表示获取没有模式的列,null标识获取所有模式的列; 可包含单字符通配符("_"),或多字符通配符("%");
             * table - 表名称;可包含单字符通配符("_"),或多字符通配符("%");
             */
            resultSet = metaData.getPrimaryKeys(null, null, "CUST_INTER_TF_SERVICE_REQ");
            
            while (resultSet.next()) {
                String tableCat = resultSet.getString("TABLE_CAT");  //表类别(可为null)
                String tableSchemaName = resultSet.getString("TABLE_SCHEM");//表模式（可能为空）,在oracle中获取的是命名空间,其它数据库未知
                String tableName = resultSet.getString("TABLE_NAME");  //表名
                String columnName = resultSet.getString("COLUMN_NAME");//列名
                short keySeq = resultSet.getShort("KEY_SEQ");//序列号(主键内值1表示第一列的主键，值2代表主键内的第二列)
                String pkName = resultSet.getString("PK_NAME"); //主键名称
                
                System.out.println(tableCat + " - " + tableSchemaName + " - " + tableName + " - " + columnName + " - "
                        + keySeq + " - " + pkName);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
    }
    
    /**
     * 获取表索引信息
     */
    public void getIndexInfo() {
        ResultSet resultSet = null;
        try {
            /*
             * 获取给定表的索引和统计信息的描述
             * 方法原型:ResultSet getIndexInfo(String catalog,String schema,String table,boolean unique,boolean approximate)
             * catalog - 表所在的类别名称;""表示获取没有类别的列,null表示获取所有类别的列。
             * schema - 表所在的模式名称(oracle中对应于Tablespace);""表示获取没有模式的列,null标识获取所有模式的列; 可包含单字符通配符("_"),或多字符通配符("%");
             * table - 表名称;可包含单字符通配符("_"),或多字符通配符("%");
             * unique - 该参数为 true时,仅返回唯一值的索引; 该参数为 false时,返回所有索引;
             * approximate - 该参数为true时,允许结果是接近的数据值或这些数据值以外的值;该参数为 false时,要求结果是精确结果;
             */
            resultSet = metaData.getIndexInfo(null, null, "CUST_INTER_TF_SERVICE_REQ", false, true);
            while (resultSet.next()) {
                String tableCat = resultSet.getString("TABLE_CAT");  //表类别(可为null)
                String tableSchemaName = resultSet.getString("TABLE_SCHEM");//表模式（可能为空）,在oracle中获取的是命名空间,其它数据库未知
                String tableName = resultSet.getString("TABLE_NAME");  //表名
                boolean nonUnique = resultSet.getBoolean("NON_UNIQUE");// 索引值是否可以不唯一,TYPE为 tableIndexStatistic时索引值为 false;
                String indexQualifier = resultSet.getString("INDEX_QUALIFIER");//索引类别（可能为空）,TYPE为 tableIndexStatistic 时索引类别为 null;
                String indexName = resultSet.getString("INDEX_NAME");//索引的名称 ;TYPE为 tableIndexStatistic 时索引名称为 null;
                /*
                 * 索引类型：
                 *  tableIndexStatistic - 此标识与表的索引描述一起返回的表统计信息
                 *  tableIndexClustered - 此为集群索引
                 *  tableIndexHashed - 此为散列索引
                 *  tableIndexOther - 此为某种其他样式的索引
                 */
                short type = resultSet.getShort("TYPE");//索引类型;
                short ordinalPosition = resultSet.getShort("ORDINAL_POSITION");//在索引列顺序号;TYPE为 tableIndexStatistic 时该序列号为零;
                String columnName = resultSet.getString("COLUMN_NAME");//列名;TYPE为 tableIndexStatistic时列名称为 null;
                String ascOrDesc = resultSet.getString("ASC_OR_DESC");//列排序顺序:升序还是降序[A:升序; B:降序];如果排序序列不受支持,可能为 null;TYPE为 tableIndexStatistic时排序序列为 null;
                int cardinality = resultSet.getInt("CARDINALITY");   //基数;TYPE为 tableIndexStatistic 时,它是表中的行数;否则,它是索引中唯一值的数量。
                int pages = resultSet.getInt("PAGES"); //TYPE为 tableIndexStatisic时,它是用于表的页数,否则它是用于当前索引的页数。
                String filterCondition = resultSet.getString("FILTER_CONDITION"); //过滤器条件,如果有的话(可能为 null)。
                
                System.out.println(tableCat + " - " + tableSchemaName + " - " + tableName + " - " + nonUnique + " - "
                        + indexQualifier + " - " + indexName + " - " + type + " - " + ordinalPosition + " - " + columnName
                        + " - " + ascOrDesc + " - " + cardinality + " - " + pages + " - " + filterCondition);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
    }
    
    
    /**
     * 获取表中列值信息
     */
    public void getColumnsInfo() {
        ResultSet resultSet = null;
        
        try {
            /*
             * 获取可在指定类别中使用的表列的描述。
             * 方法原型:ResultSet getColumns(String catalog,String schemaPattern,String tableNamePattern,String columnNamePattern)
             * catalog - 表所在的类别名称;""表示获取没有类别的列,null表示获取所有类别的列。
             * schema - 表所在的模式名称(oracle中对应于Tablespace);""表示获取没有模式的列,null标识获取所有模式的列; 可包含单字符通配符("_"),或多字符通配符("%");
             * tableNamePattern - 表名称;可包含单字符通配符("_"),或多字符通配符("%");
             * columnNamePattern - 列名称; ""表示获取列名为""的列(当然获取不到);null表示获取所有的列;可包含单字符通配符("_"),或多字符通配符("%");
             */
            resultSet = metaData.getColumns(null, null, "CUST_INTER_TF_SERVICE_REQ", null);
            
            while (resultSet.next()) {
                String tableCat = resultSet.getString("TABLE_CAT");  //表类别（可能为空）
                String tableSchemaName = resultSet.getString("TABLE_SCHEM");  //表模式（可能为空）,在oracle中获取的是命名空间,其它数据库未知
                String tableName_ = resultSet.getString("TABLE_NAME");  //表名
                String columnName = resultSet.getString("COLUMN_NAME");  //列名
                int dataType = resultSet.getInt("DATA_TYPE");     //对应的java.sql.Types的SQL类型(列类型ID)
                String dataTypeName = resultSet.getString("TYPE_NAME");  //java.sql.Types类型名称(列类型名称)
                int columnSize = resultSet.getInt("COLUMN_SIZE");  //列大小
                int decimalDigits = resultSet.getInt("DECIMAL_DIGITS");  //小数位数
                int numPrecRadix = resultSet.getInt("NUM_PREC_RADIX");  //基数（通常是10或2） --未知
                /*
                 *  0 (columnNoNulls) - 该列不允许为空
                 *  1 (columnNullable) - 该列允许为空
                 *  2 (columnNullableUnknown) - 不确定该列是否为空
                 */
                int nullAble = resultSet.getInt("NULLABLE");  //是否允许为null
                String remarks = resultSet.getString("REMARKS");  //列描述
                String columnDef = resultSet.getString("COLUMN_DEF");  //默认值
                int charOctetLength = resultSet.getInt("CHAR_OCTET_LENGTH");    // 对于 char 类型，该长度是列中的最大字节数
                int ordinalPosition = resultSet.getInt("ORDINAL_POSITION");   //表中列的索引（从1开始）
                /*
                 * ISO规则用来确定某一列的是否可为空(等同于NULLABLE的值:[ 0:'YES'; 1:'NO'; 2:''; ])
                 * YES -- 该列可以有空值;
                 * NO -- 该列不能为空;
                 * 空字符串--- 不知道该列是否可为空
                 */
                String isNullAble = resultSet.getString("IS_NULLABLE");
                
                /*
                 * 指示此列是否是自动递增
                 * YES -- 该列是自动递增的
                 * NO -- 该列不是自动递增
                 * 空字串--- 不能确定该列是否自动递增
                 */
                //String isAutoincrement = resultSet.getString("IS_AUTOINCREMENT");   //该参数测试报错
                
                
                System.out.println(tableCat + " - " + tableSchemaName + " - " + tableName_ + " - " + columnName +
                        " - " + dataType + " - " + dataTypeName + " - " + columnSize + " - " + decimalDigits + " - "
                        + numPrecRadix + " - " + nullAble + " - " + remarks + " - " + columnDef + " - " + charOctetLength
                        + " - " + ordinalPosition + " - " + isNullAble);
                
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        } finally {
            this.close(resultSet, connection);
        }
    }
    
    /**
     * 测试
     */
    public void main(String[] args) {
        getDataBaseInfo();  //获取数据库信息
        getSchemasInfo(); //获取数据库所有Schema
        getTablesList();  //获取某用户下所有的表
        getTableInfo();  //获取表信息
        getPrimaryKeysInfo(); //获取表主键信息
        getIndexInfo();  //获取表索引信息
        getColumnsInfo(); //获取表中列值信息
    }
    
}
